# Copyright (c) 2018 Presto Labs Pte. Ltd.
# Author: inkyu

import datetime
import logging
from typing import Optional

from tornado.ioloop import IOLoop

import coin.flow.subscriber as flow_subscriber
from coin.base.config import Config
from coin.flow.simple_queue import SimpleQueue
from coin.strategy.mm.subscription import SubscriptionRequest


class BaseOrderSubsystem(object):
  OrderGateway = None
  OrderSubReq = None
  _use_og_params = False
  _delay_notify_order_gateway_reset = datetime.timedelta(seconds=1)
  _delay_on_order_gateway_ready = datetime.timedelta(seconds=1)

  def __init__(self,
               currencies,
               trade_products,
               og_config_name,
               init_og_after=None,
               ioloop=None,
               queue=None,
               flow_sub=None,
               og_params=None,
               og_log_callback=None,
               logger=None,
               og_config_dict=None):
    self._logger = logger or logging.getLogger('BaseOrderSubsystem')

    self._currencies = currencies
    self._trade_products = trade_products
    if og_config_name:
      self._og_config = Config.from_config_filename(og_config_name)
    elif og_config_dict:
      self._og_config = Config.from_config_dict(og_config_dict)
    else:
      raise EnvironmentError('No og config specified.')
    self._init_og_after = init_og_after or datetime.timedelta()

    self.ioloop = ioloop or IOLoop.current()
    self.queue = queue or SimpleQueue('queue')
    self.flow_sub = flow_sub or flow_subscriber.from_queue(self.queue)

    self.on_order_gateway_reset_callback = []

    self._order_gateway = None
    self._og_params = og_params
    self._og_log_callback = og_log_callback

  @property
  def order_gateway(self):
    return self._order_gateway

  @property
  def og_params(self):
    return self._og_params

  def _init_order_gateway(self, *, order_gateway_cls=None):
    self._logger.info('Initializing %s order gateway...', self.OrderSubReq)
    order_gateway_cls = order_gateway_cls or self.OrderGateway
    order_gateway_args = [self._currencies, self._trade_products, self._og_config]
    if self._use_og_params:
      order_gateway_args.append(self._og_params)
    if self.OrderSubReq == SubscriptionRequest('Spot', 'Uniswap', 'v3-arbitrum'):
      extra_args = {"blockchain_name": "arbitrum", "api_version": "v3-arbitrum"}
    else:
      extra_args = {}
    self._order_gateway = order_gateway_cls(*order_gateway_args, **extra_args)
    # Callback
    if self._og_log_callback is not None:
      self._order_gateway.set_log_write_callback(self._og_log_callback)
      self._order_gateway.og_logger.write_account_request()
    self._order_gateway.start(self.queue, self.ioloop, self.flow_sub)

    self.ioloop.add_timeout(datetime.timedelta(seconds=1), self._on_order_gateway_ready)

  def _on_order_gateway_ready(self):
    if self._order_gateway and self._order_gateway.is_ready():
      self.ioloop.add_timeout(self._delay_notify_order_gateway_reset,
                              self._notify_order_gateway_reset)
    else:
      self.ioloop.add_timeout(self._delay_on_order_gateway_ready, self._on_order_gateway_ready)

  def _notify_order_gateway_reset(self):
    self._logger.info('Order gateway ready')
    for callback in self.on_order_gateway_reset_callback:
      callback(self.OrderSubReq, self._order_gateway)

  def start(self):
    self.ioloop.add_timeout(self._init_og_after, self._init_order_gateway)

  def loop(self, exit_after: Optional[datetime.timedelta] = None):
    if exit_after is not None:
      self.ioloop.add_timeout(exit_after, self.ioloop.stop)
    self.ioloop.start()
